package com.yaoyong.codegenerator.utils;

import com.yaoyong.codegenerator.constant.Placeholder;
import com.yaoyong.codegenerator.entity.Column;
import com.yaoyong.codegenerator.entity.Database;
import com.yaoyong.codegenerator.entity.Table;
import com.yaoyong.codegenerator.entity.Template;
import com.yaoyong.codegenerator.properties.PathProperties;
import com.yaoyong.codegenerator.properties.ProjectProperties;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 模板工具类:用于读取模板文件
 *
 * @author 姚勇
 */
public class TemplateUtil {

    /**
     * 读取指定目录下所有模板文件
     *
     * @param basePath
     * @return
     */
    public static List<Template> getTemplateList(String basePath) {
        List<Template> list = new ArrayList<>();
        // 递归显示路劲下所有文件夹及其中文件
        File root = new File(basePath);
        return getTemplates(list, basePath, root);
    }

    private static List<Template> getTemplates(List<Template> list, String basePath, File root) {
        File[] files = root.listFiles();
        if (files == null) {
            return new ArrayList<Template>();
        }
        for (File file : files) {
            if (file != null && file.isDirectory()) {
                getTemplates(list, basePath, file);
            }
            if (file != null && file.isFile()) {
                Template template = new Template();
                template.setName(file.getName());
                try {
                    template.setContent(FileUtil.getContent(file));
                } catch (IOException e) {
                    System.err.println("文件名:" + file.getName() + "获取内容失败");
                }
                template.setAbsolutePath(file.getAbsolutePath());
                template.setRelativePath(file.getAbsolutePath().replace(basePath, ""));
                list.add(template);
            }
        }
        return list;
    }

    /**
     * 替换表级模板
     *
     * @param content
     * @param tableTemplates
     * @param tables
     * @return
     */
    public static String replaceTableContent(String content, List<Template> tableTemplates, List<Table> tables) {
        // 循环所有表级模板
        for (Template template : tableTemplates) {
            // 替换符号
            String thf = Placeholder.ANGLE_BRACKET_LEFT + template.getName() + Placeholder.ANGLE_BRACKET_RIGHT;

            //如果项目模板中含有表级模板替换符
            if (content.contains(thf)) {
                // 循环体 表级模板
                String tableTemplateContent = template.getContent();

                StringBuilder createContent = new StringBuilder();
                for (Table table : tables) {

                    // 根据模板生成新内容
                    //表名使用驼峰命名,下划线后面转大写
                    String tableName = FileUtil.getCamelName(table.getName());
                    //替换小写的表名,替换大写的表名,替换主键,替换备注
                    String newContent = tableTemplateContent.replace(Placeholder.TABLE_NAME,table.getName())
                            .replace(Placeholder.TABLE_LOWER, tableName).replace(Placeholder.TABLE_UPPER, FileUtil.getClassName(tableName));

                    //有主键列
                    if (table.getKey() != null) {
                        //主键
                        newContent = newContent.replace(Placeholder.TABLE_KEY, table.getKey());
                    }
                    // 备注
                    newContent = newContent.replace(Placeholder.TABLE_COMMENT, table.getComment()==null?"":table.getComment());
                    createContent.append(newContent);
                }

                // 替换主体内容
                content = content.replace(thf, createContent.toString());
            }
        }
        return content;
    }

    /**
     * 替换列级模板
     *
     * @param outContent
     * @param columnTemplates
     * @param table
     * @return
     */
    public static String replaceColumnContent(String outContent, List<Template> columnTemplates, Table table) {
        //循环所有列级模板
        for (Template template : columnTemplates) {
            // 替换符号
            String thf = Placeholder.ANGLE_BRACKET_LEFT + template.getName() + Placeholder.ANGLE_BRACKET_RIGHT;

            // 如果表级模板中包含列级模板
            if (outContent.contains(thf)) {
                // 循环体 列级模板
                String columnTemplateContent = template.getContent();
                StringBuilder createContent = new StringBuilder();
                for (Column column : table.getColumns()) {
                    // 根据列级模板生成新内容
                    if (isLoop(template, column)) {
                        System.out.println("列级模板内容：" + columnTemplateContent);
                        //使用驼峰命名,下划线后面转大写
                        String colName = FileUtil.getCamelName(column.getName());
                        String newContent = columnTemplateContent.replace(Placeholder.COLUMN_LOWER, colName).replace(Placeholder.COLUMN_UPPER, FileUtil.getClassName(colName))
                                .replace(Placeholder.COLUMN_TYPE, column.getType()).replace(Placeholder.COLUMN_DB_TYPE, column.getDbType())
                                .replace(Placeholder.COLUMN_COMMENT, column.getComment()==null?"":column.getComment());
                        createContent.append(newContent);
                        System.out.println("列级模板替换后内容：" + newContent);
                    }
                }

                // 替换主体内容
                outContent = outContent.replace(thf, createContent.toString());
            }
        }
        //表名使用驼峰命名,下划线后面转大写
        String tableName = FileUtil.getCamelName(table.getName());
        outContent = outContent.replace(Placeholder.TABLE_NAME,table.getName())
                .replace(Placeholder.TABLE_LOWER, tableName).replace(Placeholder.TABLE_UPPER, FileUtil.getClassName(tableName))
                .replace(Placeholder.TABLE_COMMENT, table.getComment()==null?"":table.getComment());
        if (table.getKey() != null) {
            // 备注
            outContent = outContent.replace(Placeholder.TABLE_KEY, table.getKey());
        }
        return outContent;
    }

    /**
     * 替换全局占位
     *
     * @param outContent
     * @param project
     * @param database
     * @return
     */
    public static String replaceGlobalContent(String outContent, PathProperties path, ProjectProperties project, Database database) {
        Map<String, String> globalReplaceMap = new HashMap<>();
        globalReplaceMap.put(Placeholder.PROJECT, project.getName());
        globalReplaceMap.put(Placeholder.PROJECT_COMMENT, project.getComment());
        globalReplaceMap.put(Placeholder.PROJECT_AUTHOR, project.getAuthor());
        globalReplaceMap.put(Placeholder.PROJECT_GROUP_ID, project.getGroupId());
        globalReplaceMap.put(Placeholder.PROJECT_ARTIFACT_ID, project.getArtifactId());
        globalReplaceMap.put(Placeholder.PROJECT_VERSION, project.getVersion());
        globalReplaceMap.put(Placeholder.PROJECT_PACKAGE, project.getPackageName());
        //包路径可根据包名转换
        String packagePath = path.getPackagePath();
        if (!StringUtils.hasLength(packagePath)){
            packagePath = project.getPackageName().replace(".","/");
        }
        globalReplaceMap.put(Placeholder.PROJECT_PACKAGE_PATH, packagePath);
        //层级目录可以根据包名转换
        Map<String, String> hierarchyPath = path.getHierarchyPath();
        if (hierarchyPath.isEmpty()) {
            String[] paths = project.getPackageName().replace(".", ",").split(",");
            for (int i = 1; i <= paths.length; i++) {
                hierarchyPath.put("path_" + i, paths[i - 1]);
            }
        }
        for (String key : hierarchyPath.keySet()) {
            globalReplaceMap.put(Placeholder.SQUARE_BRACKETS_LEFT + key + Placeholder.SQUARE_BRACKETS_RIGHT, hierarchyPath.get(key));
        }
        globalReplaceMap.put(Placeholder.DB_TYPE, database.getDatabaseType().toString());
        globalReplaceMap.put(Placeholder.DB_NAME, database.getName());
        globalReplaceMap.put(Placeholder.DB_DRIVER, database.getDriverClassName());
        globalReplaceMap.put(Placeholder.DB_URL, database.getUrl());
        globalReplaceMap.put(Placeholder.DB_USERNAME, database.getUsername());
        globalReplaceMap.put(Placeholder.DB_PASSWORD, database.getPassword());

        for (String key : globalReplaceMap.keySet()) {
            outContent = outContent.replace(key, globalReplaceMap.get(key)==null?"":globalReplaceMap.get(key));
        }
        return outContent;
    }

    /**
     * 判断是否要循环当前列
     *
     * @param template
     * @param column
     * @return
     */
    private static boolean isLoop(Template template, Column column) {
        //不循环的属性
        List<String> undo = Arrays.asList("id","created","updated");

        // 控制开关
        boolean b = true;

        //循环实体类私有属性
        if (template.getName().contains(".private")) {
            if (undo.contains(FileUtil.getCamelName(column.getName()))){
                //排除不需要的字段 id,created,updated
                b =false;
            }
        }

        // 只循环主键
        if (template.getName().contains(".key")) {
            if (!column.getKey()) {
                // 不是主键
                b = false;
            }
        }
        // 只循环非主键
        if (template.getName().contains(".nokey")) {
            if (column.getKey() || undo.contains(FileUtil.getCamelName(column.getName()))) {
                // 不是主键
                b = false;
            }
        }

        // 只循环String 类型
        if (template.getName().contains(".String")) {
            if (!column.getType().equals("String")) {
                // 不是String
                b = false;
            }
        }

        // 只循环 DateTime 类型
        if (template.getName().contains(".DateTime")) {
            if (!column.getType().equals("java.time.LocalDateTime")) {
                // 不是 DateTime
                b = false;
            }
        }

        // 只循环 Boolean 类型
        if (template.getName().contains(".Boolean")) {
            if (!column.getType().equals("Boolean")) {
                // 不是 Boolean
                b = false;
            }
        }
        return b;
    }
}
